package tiny_file

import (
	"context"
	"github.com/glebarez/sqlite"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"time"

	//"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"sync"
)

func openDB(name string) (*gorm.DB, error) {
	db, err := gorm.Open(sqlite.Open(name), &gorm.Config{})
	if err != nil {
		return nil, err
	}
	_ = db.Exec("PRAGMA synchronous = normal;")
	return db, err
}

type sqliteHandle struct {
	db  *gorm.DB
	log *zap.Logger
	sync.Mutex
}

func NewSqliteHandle(name string, log *zap.Logger) *sqliteHandle {
	db, err := openDB(name)
	if err != nil {
		return nil
	}
	return &sqliteHandle{db: db, log: log}
}

func (s *sqliteHandle) Migrator(ctx context.Context) {
	s.db.WithContext(ctx).AutoMigrate(
		&Idx{},
		&LargeFileInfo{},
	)
}

func (s *sqliteHandle) Insert(idx Idx) error {
	return s.db.Model(&Idx{}).Create(&idx).Error
}

func (s *sqliteHandle) BatchInsert(idx []Idx) error {
	return s.db.CreateInBatches(idx, 500).Error
}

func (s *sqliteHandle) Get(pieceId uuid.UUID) (idx Idx, err error) {
	err = s.db.Model(&Idx{}).Where("piece_id = ? and tmp = 0", pieceId).First(&idx).Error
	return
}

func (s *sqliteHandle) GetIdxCountWithoutDelOrExpired(file string) (total int64, err error) {
	err = s.db.Model(&Idx{}).Where("file = ? and tmp = 0 and (expire = 0 or expire > ?)", file, time.Now().Unix()).
		Count(&total).Error
	return
}

func (s *sqliteHandle) Del(pieceId uuid.UUID) error {
	return s.db.Model(&Idx{}).Where("piece_id = ?", pieceId).Update("del", 1).Error
}

func (s *sqliteHandle) DelIdxTmp() error {
	return s.db.Where("tmp = 1").Delete(&Idx{}).Error
}

func (s *sqliteHandle) PreAllocation(key string, pieceSize int) (*LargeFileInfo, error) {
	s.Lock()
	defer s.Unlock()

	m := map[string]interface{}{
		"last_pos": gorm.Expr("last_pos+?", int64(pieceSize)),
		"count":    gorm.Expr("count+1"),
	}
	result := s.db.Model(&LargeFileInfo{}).Where("file=?", key).Updates(m)
	if result.RowsAffected == 0 {
		s.log.Debug("create new", zap.String("file", key))
		s.db.Model(&LargeFileInfo{}).Create(&LargeFileInfo{
			File:    key,
			LastPos: int64(pieceSize),
			Count:   1,
		})
	}

	ret := &LargeFileInfo{}
	err := s.db.Model(&LargeFileInfo{}).Where("file=?", key).First(ret).Error
	ret.LastPos -= int64(pieceSize)
	return ret, err
}

// GetLargeFileInfos 获取进行清理的大文件列表
func (s *sqliteHandle) GetLargeFileInfos() (results []*LargeFileInfo, err error) {
	err = s.db.Model(&LargeFileInfo{}).Where("file != ?", Today()).Order("last_cleanup").Find(&results).Error
	return
}

// Transaction 处理事物方法
func (s *sqliteHandle) Transaction(fn func(tx *gorm.DB) error) error {
	return s.db.Transaction(fn)
}
